[pull] main from MetaMask:main#782
Merged
Merged
Conversation
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until this PR meets the canonical
Definition of Ready For Review in `docs/readme/ready-for-review.md`.
In short: the template must be materially complete (not just section
titles
present), all status checks must be currently passing, and the only
expected
follow-up commits must be reviewer-driven.
-->
## **Description**
Bump qs 15.2
<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->
## **Changelog**
<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`
If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`
(This helps the Release Engineer do their job more quickly and
accurately)
-->
CHANGELOG entry:
## **Related issues**
Fixes:
## **Manual testing steps**
```gherkin
Feature: my feature name
Scenario: user [verb for user action]
Given [describe expected initial app state]
When user [verb for user action]
Then [describe expected outcome]
```
## **Screenshots/Recordings**
<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->
### **Before**
<!-- [screenshots/recordings] -->
### **After**
<!-- [screenshots/recordings] -->
## **Pre-merge author checklist**
<!--
Every checklist item must be consciously assessed before marking this PR
as
"Ready for review". A checked box means you deliberately considered that
responsibility, not that you literally performed every action listed.
Unchecked boxes are ambiguous: they are not an implicit "N/A" and they
are not
a silent "skip". See `docs/readme/ready-for-review.md` for the full
checklist
semantics.
-->
- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
#### Performance checks (if applicable)
- [ ] I've tested on Android
- Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example
For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).
## **Pre-merge reviewer checklist**
<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Dependency-only patch with no app code changes; `qs` is a transitive
query-string parser, so blast radius is limited to how dependents
serialize/parse URLs.
>
> **Overview**
> Upgrades the **`qs`** query-string library from **6.14.1** to
**6.15.2** across Yarn resolutions, direct `package.json` dependencies,
and **`yarn.lock`**. No application source files change—only dependency
pins and lockfile metadata (version, resolution checksum).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ea4207a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…16 KB warning (#30590) ## **Description** Google Play flagged two prebuilt `.so` files in our production AAB as non-compliant with Android's 16 KB page-size requirement, both in the `x86_64` ABI: - `base/lib/x86_64/libconceal.so` — from `react-native-keychain` via `com.facebook.conceal:1.1.3` (last released in 2016, archived upstream). - `base/lib/x86_64/libsecp256k1.so` — from `react-native-fast-crypto`, shipped as an `IMPORTED` prebuilt in its CMake config (not rebuilt from source). Neither library can be realistically rebuilt with 16 KB alignment: - Facebook Conceal has been unmaintained since 2018; there is no source-of-truth fork with 16 KB alignment. - `react-native-fast-crypto` consumes `libsecp256k1.so` as a prebuilt binary; the existing yarn patch already adds `-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON` for the bridge code we compile, but it does not regenerate the imported `libsecp256k1.so` files. x86_64 is only ever used by Chromebook ARC++/ARCVM and emulators — no real phone ships with an x86_64 CPU. Dropping the x86_64 ABI from production AABs silences the Play warning without affecting users. ### What this PR does Two changes that need to land together: 1. **`android/gradle.properties.release`** — remove `x86_64` from `reactNativeArchitectures` so React Native's own native libs (Hermes, RN core) aren't built for that ABI in the production AAB. 2. **`android/app/build.gradle`** — add `ndk.abiFilters(*reactNativeArchitectures())` inside `defaultConfig`. Without this, AGP packages every ABI shipped in dependency AARs regardless of `reactNativeArchitectures`, which is what put the x86_64 `libconceal.so` and `libsecp256k1.so` into the AAB in the first place. A small refactor of the `reactNativeArchitectures()` helper to return a `List` (via `.toList()`) makes the Groovy spread `*reactNativeArchitectures()` into `abiFilters(String...)` robust across AGP/Groovy versions. ### What this PR does NOT change - `android/gradle.properties` (default) — local dev still gets x86_64 emulator support. - `android/gradle.properties.github` (CI E2E, `x86_64` only) — the new `abiFilters` resolves to `["x86_64"]` in that context, so emulator tests continue to work. - `android/gradle.properties.github.dual-versions` (`armeabi-v7a,arm64-v8a`) — already excludes x86_64. - `scripts/build.sh` `-PreactNativeArchitectures=...` CLI overrides — still respected by the helper. ### Follow-up work (out of scope) The same `.so` files still ship for `arm64-v8a` with 4 KB alignment. If/when Play extends the warning to arm64, or enforces the runtime 16 KB device requirement more aggressively on Android 15+ devices in 16 KB mode, these will need structural fixes: - Replace `react-native-fast-crypto` with `react-native-quick-crypto@1.x` (we only consume `scrypt`). - Drop the Conceal dependency from `react-native-keychain` (yarn patch or upgrade to v10) — safe because `minSdk = 24` means the Conceal cipher path is never selected at runtime. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: Google Play 16 KB page-size warning for `base/lib/x86_64/libconceal.so` and `base/lib/x86_64/libsecp256k1.so`. ## **Manual testing steps** ```gherkin Feature: Production AAB no longer contains x86_64 ABI Scenario: A production release build is generated Given a clean checkout of this branch When the production AAB is built via the standard release workflow (the same one that does `cp android/gradle.properties.release android/gradle.properties` before `./gradlew bundleProdRelease`) Then `unzip -l app-prod-release.aab | grep '/lib/'` shows only `lib/arm64-v8a/`, `lib/armeabi-v7a/`, and `lib/x86/` entries And no `lib/x86_64/` entries appear in the output And no `libconceal.so` or `libsecp256k1.so` files appear under any `lib/x86_64/` path Scenario: Local dev on x86_64 emulator still works Given a developer running `yarn android` on an Intel Mac with an x86_64 emulator When the debug variant is built using the default `android/gradle.properties` Then x86_64 native libs are still produced and the app installs and runs Scenario: CI E2E on x86_64 emulator still works Given CI overlays `android/gradle.properties.github` (reactNativeArchitectures=x86_64) When `./gradlew assembleProdDebug` runs Then the resulting APK contains a `lib/x86_64/` directory and Detox tests pass ``` ## **Screenshots/Recordings** ### **Before** Play Console flagged: - `base/lib/x86_64/libconceal.so` - `base/lib/x86_64/libsecp256k1.so` ### **After** Production AAB contains no `lib/x86_64/` directory — both warnings cleared. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable — N/A (build-config change; verification is via the `unzip -l` check on the produced AAB) - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable — N/A - [ ] I've applied the right labels on the PR — `team-mobile-platform` #### Performance checks (if applicable) - [x] I've tested on Android — verified locally that the abiFilters change does not break the `gradle.properties.github` (x86_64-only) path used by CI E2E. - [ ] I've tested with a power user scenario — N/A (no JS behavior change) - [ ] I've instrumented key operations with Sentry traces — N/A ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. Made with [Cursor](https://cursor.com) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Build-time ABI and packaging configuration only; no runtime app logic, auth, or data-path changes, with emulator/CI paths still driven by their gradle overlays. > > **Overview** > Production Play Store builds stop shipping **x86_64** native libraries so Google Play’s **16 KB page-size** warnings go away for prebuilt `libconceal.so` and `libsecp256k1.so` that only appeared under `lib/x86_64/`. > > **`android/gradle.properties.release`** drops `x86_64` from `reactNativeArchitectures` (now `armeabi-v7a`, `arm64-v8a`, `x86`). **`android/app/build.gradle`** adds `defaultConfig.ndk.abiFilters(*reactNativeArchitectures())` so AGP does not still package every ABI from third-party AARs when RN’s arch list is narrower. The `reactNativeArchitectures()` helper now returns a Groovy `List` via `.toList()` so the spread into `abiFilters` is reliable. > > Default and CI gradle property files are unchanged in this diff: local debug can still target x86_64 emulators, and CI E2E overlays that still set `x86_64` only continue to filter to that ABI. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 40dd023. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Cursor <cursoragent@cursor.com>
…m sheet closes order screen (#30561) ## **Description** Fixes a bug where closing the order type bottom sheet (by tapping outside, pressing the close button, or selecting an option) would also close the entire order screen. **Root cause**: `PerpsOrderTypeBottomSheet` passed `shouldNavigateBack={!externalSheetRef}` to the `BottomSheet` component. When embedded in `PerpsOrderView` (no external sheet ref), this evaluated to `true`, causing the BottomSheet infrastructure to call `navigation.goBack()` on dismiss — popping the order screen from the navigation stack. The parent's `onClose` callback then fired redundantly. **Fix**: Set `shouldNavigateBack={false}` unconditionally and always pass the `onClose` prop through to BottomSheet, so the parent controls what happens on dismiss. ## **Changelog** CHANGELOG entry: Fixed order type bottom sheet dismissal incorrectly closing the order screen ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3113 ## **Manual testing steps** ```gherkin Feature: Order type bottom sheet dismissal Scenario: User selects an order type from the bottom sheet Given the user is on the perps order screen with the order type bottom sheet open When user taps on "Market" or "Limit" order type Then the bottom sheet closes And the order screen remains visible and functional Scenario: User reopens the order type bottom sheet after dismissal Given the user just dismissed the order type bottom sheet When user taps the order type button in the order header Then the order type bottom sheet opens again And the user can select a different order type ``` ## **Screenshots/Recordings** ### **Before** Order type sheet dismissal (tap outside or select option) closes the entire order screen — user is navigated back to market details. https://github.com/user-attachments/assets/a8a36f0a-6be5-418b-8c95-550bd4dc8056 ### **After** Order type sheet dismissal keeps the order screen open. User can reopen the sheet and select a different order type. https://github.com/user-attachments/assets/d2d18a62-42fa-433b-8ba5-812e01d19f1d ## **Validation Recipe** <details><summary>recipe.json (18 steps — order type sheet open/dismiss/reopen cycle)</summary> ```json { "schema_version": 1, "title": "TAT-3113: Order type bottom sheet dismissal does not close order screen", "jira": "TAT-3113", "description": "Verifies that dismissing the order type bottom sheet (by selecting an option) keeps the user on the order screen instead of navigating back.", "acceptance_criteria": [ "AC1: Selecting an order type from the bottom sheet closes the sheet but does NOT close the order screen", "AC2: After dismissal, the order form remains visible and functional (can reopen the sheet)" ], "validate": { "workflow": { "pre_conditions": ["wallet.unlocked", "perps.ready_to_trade"], "entry": "nav-to-details", "nodes": { "nav-to-details": { "action": "navigate", "target": "PerpsMarketDetails", "params": { "market": { "symbol": "BTC" } }, "next": "wait-details-loaded" }, "wait-details-loaded": { "action": "wait_for", "test_id": "perps-market-details-long-button", "timeout_ms": 8000, "next": "open-order" }, "open-order": { "action": "press", "test_id": "perps-market-details-long-button", "next": "wait-order-form" }, "wait-order-form": { "action": "wait_for", "test_id": "perps-order-header-order-type-button", "timeout_ms": 5000, "next": "open-order-type-sheet" }, "open-order-type-sheet": { "action": "press", "test_id": "perps-order-header-order-type-button", "next": "wait-sheet" }, "wait-sheet": { "action": "wait_for", "test_id": "perps-order-type-market", "timeout_ms": 3000, "next": "screenshot-sheet-open" }, "screenshot-sheet-open": { "action": "screenshot", "filename": "evidence-order-type-sheet-open.png", "note": "Order type bottom sheet is open showing Market and Limit options", "next": "dismiss-select-market" }, "dismiss-select-market": { "action": "press", "test_id": "perps-order-type-market", "next": "wait-dismiss" }, "wait-dismiss": { "action": "wait", "duration_ms": 1000, "next": "verify-still-on-order" }, "verify-still-on-order": { "action": "eval_sync", "expression": "(function(){ var r = globalThis.__AGENTIC__.getRoute(); return JSON.stringify({ route: r.name }); })()", "assert": { "operator": "eq", "field": "route", "value": "RedesignedConfirmations" }, "next": "verify-order-form-visible" }, "verify-order-form-visible": { "action": "eval_sync", "expression": "(function(){ var el = globalThis.__AGENTIC__.findFiberByTestId('perps-order-view-place-order-button'); return JSON.stringify({ visible: !!el }); })()", "assert": { "operator": "eq", "field": "visible", "value": true }, "next": "screenshot-after-dismiss" }, "screenshot-after-dismiss": { "action": "screenshot", "filename": "evidence-order-still-visible.png", "note": "Order form still visible after selecting order type — order screen was NOT closed", "next": "reopen-sheet" }, "reopen-sheet": { "action": "press", "test_id": "perps-order-header-order-type-button", "next": "wait-reopen" }, "wait-reopen": { "action": "wait_for", "test_id": "perps-order-type-limit", "timeout_ms": 3000, "next": "select-limit" }, "select-limit": { "action": "press", "test_id": "perps-order-type-limit", "next": "wait-dismiss-2" }, "wait-dismiss-2": { "action": "wait", "duration_ms": 1000, "next": "verify-still-on-order-2" }, "verify-still-on-order-2": { "action": "eval_sync", "expression": "(function(){ var r = globalThis.__AGENTIC__.getRoute(); return JSON.stringify({ route: r.name }); })()", "assert": { "operator": "eq", "field": "route", "value": "RedesignedConfirmations" }, "next": "screenshot-final" }, "screenshot-final": { "action": "screenshot", "filename": "evidence-limit-selected-still-on-order.png", "note": "After selecting Limit order type, order form still visible with limit type active", "next": "done" }, "done": { "action": "end", "status": "pass" } } } } } ``` </details> ## **Validation Logs** Command: ```bash IOS_SIMULATOR=mm-3 bash scripts/perps/agentic/validate-recipe.sh .task/feat/tat-3113-0521-165010/artifacts/ --skip-manual ``` <details><summary>Full output (18/18 passed)</summary> ``` Running recipe: TAT-3113: Order type bottom sheet dismissal does not close order screen Pre-conditions: wallet.unlocked, perps.ready_to_trade Workflow nodes: 19 Pre-conditions: PASS [nav-to-details] navigate to PerpsMarketDetails — PASS [wait-details-loaded] wait for perps-market-details-long-button — PASS [open-order] press perps-market-details-long-button — PASS [wait-order-form] wait for perps-order-header-order-type-button — PASS [open-order-type-sheet] press perps-order-header-order-type-button — PASS [wait-sheet] wait for perps-order-type-market — PASS [screenshot-sheet-open] screenshot evidence-order-type-sheet-open.png — PASS [dismiss-select-market] press perps-order-type-market — PASS [wait-dismiss] wait 1000ms — PASS [verify-still-on-order] eval_sync route=RedesignedConfirmations — PASS [verify-order-form-visible] eval_sync visible=true — PASS [screenshot-after-dismiss] screenshot evidence-order-still-visible.png — PASS [reopen-sheet] press perps-order-header-order-type-button — PASS [wait-reopen] wait for perps-order-type-limit — PASS [select-limit] press perps-order-type-limit — PASS [wait-dismiss-2] wait 1000ms — PASS [verify-still-on-order-2] eval_sync route=RedesignedConfirmations — PASS [screenshot-final] screenshot evidence-limit-selected-still-on-order.png — PASS Results: 18/18 passed Recipe: PASS ``` </details> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Single-component BottomSheet prop change for a nested sheet; no auth, payments, or shared infrastructure changes. > > **Overview** > Fixes perps order flow navigation so dismissing the **order type** bottom sheet no longer pops the whole order screen. > > `PerpsOrderTypeBottomSheet` now always passes `shouldNavigateBack={false}` to `BottomSheet` and always wires `onClose`, instead of enabling `navigation.goBack()` when no external `sheetRef` is provided. Dismissal (tap outside, close, or picking Market/Limit) only runs the parent `onClose` handler and leaves the user on the order form. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 77e3642. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** Adds [agent-device](https://github.com/callstackincubator/agent-device) as a dev dependency, exposing it as a CLI for AI agents to control iOS/Android simulators during development. Device control (opening the app, navigating screens, taking snapshots, interacting with UI elements, capturing visual evidence) runs through: ```bash yarn agent-device <command> --json ``` The package is installed locally so no global install is required. Version is pinned to an exact version (`0.14.8`, no semver range) as recommended by the security team — combined with Yarn's lockfile checksum, this prevents undetected same-tag re-deployments. Ideally to be used with the [simulator-control skill](Consensys/skills#11) ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/MCWP-450 ## **Manual testing steps** ```gherkin Feature: agent-device CLI availability Background: Given the branch is checked out and `yarn install` has been run And the simulator-control skill is installed via `yarn skills --domain testing` And an iOS simulator is booted And Metro is running via `yarn watch:clean` Scenario: AI agent controls the iOS simulator via CLI Given the Cursor IDE is open on this project When the user sends the following prompt to the Cursor agent: """ Open the MetaMask app on the iOS simulator and take a screenshot of the home screen """ And the agent runs `yarn agent-device devices --platform ios` to list booted simulators And the agent runs `yarn agent-device open io.metamask.MetaMask --platform ios` And the MetaMask app opens on the simulator And the agent runs `yarn agent-device screenshot` and returns the image ``` ## **Screenshots/Recordings** ### **Before** N/A ### **After** <img width="500" alt="image" src="https://github.com/user-attachments/assets/c6e06db5-4cef-44fc-ab79-b1d66883e32e" /> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - [x] I've tested with a power user scenario - [x] I've instrumented key operations with Sentry traces for production performance metrics ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: adds a pinned dev-only CLI dependency plus lockfile updates and documentation, with no runtime/app logic changes; main concern is install friction due to `agent-device`'s Node engine declaration. > > **Overview** > Adds the `agent-device` package (pinned to `0.14.8`) as a dev dependency to provide a local `yarn agent-device` CLI for controlling iOS/Android simulators. > > Updates `yarn.lock` for the new dependency and related transitive bumps (notably `fast-xml-parser`/`fast-xml-builder`), adds `agent-device` to `.depcheckrc.yml` ignores since it’s CLI-only, and documents usage in `docs/readme/agent-device.md` including the Node engine note. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7c9637f. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description** Adds toast notifications for Money Account deposit and withdrawal transactions, mirroring the existing Earn (`useEarnToasts` / `useMusdConversionStatus`) pattern. A new `useMoneyTransactionStatus` hook subscribes to `TransactionController:transactionStatusUpdated` and `transactionConfirmed`, filters for `TransactionType.moneyAccountDeposit` and `TransactionType.moneyAccountWithdraw`, deduplicates by transaction id + status, and surfaces: - `approved` → "Transaction in progress" (spinner, persistent) - `confirmed` → "Transaction complete" with the decoded mUSD amount formatted as fiat. Falls back to a `X.XX mUSD` label when no fiat rate is available so the toast still surfaces a real value. - `failed` → "Transaction failed" with a DSRN Primary "Try again" button. The button navigates the user to the relevant Money picker sheet (Add money / Transfer) so they can re-initiate; a true retry that preserves the prior amount/token is tracked as a follow-up. The hook is mounted globally via `<MoneyTransactionMonitor />` placed alongside `<EarnTransactionMonitor />` in `Nav/Main/index.js` so toasts surface even after the user has navigated away from Money screens. Retry navigation goes through `NavigationService` (not `useNavigation`) because the hook runs outside the `MainNavigator`'s screen scope. Active transaction types covered today: `moneyAccountDeposit` (Convert crypto + Move mUSD) and `moneyAccountWithdraw` (Between accounts). Out of scope: Ramp "Deposit funds" purchases (no `moneyAccountDeposit` transaction is dispatched) and Perps / Predict transfers (currently "Under construction" stubs; a TODO in `useMoneyTransactionStatus.ts` marks where to derive the withdraw success destination once they ship). ## **Changelog** CHANGELOG entry: Added in-app toasts for Money Account deposit and withdrawal transactions, including a "Try again" action when a transaction fails. ## **Related issues** Fixes: [MUSD-810](https://consensyssoftware.atlassian.net/browse/MUSD-810) ## **Manual testing steps** ```gherkin Feature: Money Account transaction toasts Scenario: User completes a Money Account deposit Given the user is on the Money home screen with a non-zero source balance When the user taps "Add money" → "Convert crypto" and confirms the conversion Then a "Transaction in progress" toast appears with a spinner And when the transaction confirms, a "Transaction complete" toast appears And the body reads "{amount} added to Money account." Scenario: User completes a Money Account withdrawal Given the user has a non-zero Money Account balance When the user taps "Transfer" → "Between accounts" and confirms Then a "Transaction in progress" toast appears with a spinner And when the transaction confirms, a "Transaction complete" toast appears And the body reads "{amount} moved to Between accounts." Scenario: A deposit transaction fails Given the user has confirmed a deposit When the transaction fails on-chain Then a "Transaction failed" toast appears with a "Try again" button And tapping "Try again" navigates to the Add money picker sheet Scenario: A withdrawal transaction fails Given the user has confirmed a withdrawal When the transaction fails on-chain Then a "Transaction failed" toast appears with a "Try again" button And tapping "Try again" navigates to the Transfer picker sheet ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - [x] I've tested with a power user scenario - [x] I've instrumented key operations with Sentry traces for production performance metrics ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [MUSD-810]: https://consensyssoftware.atlassian.net/browse/MUSD-810?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds global transaction event listeners and suppresses existing in-app notifications for `moneyAccountDeposit`/`moneyAccountWithdraw`, which could impact user-facing transaction feedback and requires careful validation across transaction lifecycles (including batched transactions). > > **Overview** > **Money Account deposits/withdrawals now surface in-app toasts** for `approved` (deferred in-progress), `confirmed` (success with decoded/fiat-formatted mUSD amount), and `failed` (error) transaction states via a new `useMoneyTransactionStatus` hook and `MoneyTransactionMonitor` mounted in `Nav/Main`. > > This introduces a new `useMoneyToasts` builder for consistent toast UI + haptics and adds corresponding i18n strings, while also updating `NotificationManager` to *skip* legacy transaction notifications for `moneyAccountDeposit` and `moneyAccountWithdraw` and exporting `TELLER_ABI` for calldata decoding. Tests were added to cover toast option building, event subscription/dedup/timer cleanup, and batch/nested transaction handling. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 197e27e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…#30580) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** Re-run CI on finished runs when e2e labels are modified in the PR <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Changes only GitHub Actions orchestration for label-triggered CI reruns; no app, auth, or data paths. > > **Overview** > Tightens the **rerun CI on E2E skip label** workflow so cancellation and rerun behave correctly when multiple `ci.yml` runs exist on the branch. > > The **wait-for-cancel** step no longer tracks a single run ID from the find step. It polls `gh run list` for any `ci.yml` runs on the head branch that are still `in_progress` or `queued`, and only proceeds when that count hits zero (or times out after 600s). > > The **rerun** step now runs only when the pull request is **open** and a latest run ID was found. Rerun is invoked directly without swallowing failures, so a non-retriable run surfaces as a workflow error instead of a soft log message. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit cc82224. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )